#ifndef INTERRAPT_H
#define INTERRAPT_H

#include <Types.h>
#include <Segments.h>
#include <kstdio.h>

#define IDT_SIZE	0x07FF		/*Размер таблицы прерываний в байтах*/
#define IDT_MAX		IDT_SIZE / 8	/*Количество дескрипторов прерываний в таблице*/
#define RES_INT		32		/*Зарезервированные номера прерываний*/

#define i8259A_M0	0x20
#define i8259A_M1	0x21
#define i8259A_S0	0xa0
#define i8259A_S1	0xa1
#define i8259A_ICW1	0x11
#define i8259A_ICW2_M	0x20
#define i8259A_ICW3_M	(1 << 2)
#define i8259A_ICW4_M	0x5
#define i8259A_ICW2_S	0x28
#define i8259A_ICW3_S	0x2
#define i8259A_ICW4_S	0x1

typedef struct{
	uint32 fs, es, ds, edi, esi, ebp, esp, ebx, edx, ecx, eax;
}reg_t;

static inline uint8 inb(uint16 port){
	uint8 val;
	asm volatile("inb %w1, %0":"=a" (val):"Nd" (port));
	return val; 
}

static inline void outb(uint8 val, uint16 port){
	asm volatile("outb %b0, %w1": :"a" (val), "Nd" (port));
}

/*Сохраняет все регистры общего назначения в стеке
* Вызывает функцию обработчика прерывания
* Разрешает контроллеру возобновить прерывания
* Восстанавливает регистры из стека
* Возврат из прерывания
*/
#define INT_HANDLER(IFUNC) \
    void IFUNC (void); \
    asm("_Z10" #IFUNC"v:"); \
    asm("pusha"); \
    asm("call _Z11_" #IFUNC"v");\
    asm("movb $0x20, %al"); \
    asm("outb %al, $0x20"); \
    asm("popa"); \
    asm("iret"); \
    void _ ## IFUNC()

/*
*Тоже самое, но еще извлекает из стека код ошибки
*/

extern uint32	INT_ERR;

#define INT_HANDLER_ERR(func) void func (void);\
 asm(#func ": pop INT_ERR \n pusha");\
 asm("call _" #func " \n popa \n iret \n");\
 void _ ## func(void)

/*Макрос для входа в прерывание*/
#define interrapt_in()	\
	asm("pushal");	\
	asm("cld");	\
	asm("push %ax");	\
	asm("movw $0x68, %ax; movw %ax, %es; movw %ax, %fs; movw %ax, %gs; movw %ax, %ds");	\
	asm("pop %ax");

#define int_err_in(error, reg)	\
	asm volatile("pushl %%eax\n	\
		movl	4(%%ebp), %%eax\n	\
		movl	%%eax, %0	\n	\
		pushl	%%ecx	\n	\
		pushl	%%edx	\n	\
		pushl	%%ebx	\n	\
		pushl	%%esp	\n	\
		pushl	%%ebp	\n	\
		pushl	%%esi	\n	\
		pushl	%%edi	\n	\
		pushl	%%ds	\n	\
		pushl	%%es	\n	\
		pushl	%%fs	\n	\
		movl	%%esp, %1	\n	\
		cld	\n	\
		movw	$0x68, %%ax; movw %%ax, %%es; movw %%ax, %%ds; movw %%ax, %%fs;":	\
		"=m" ( error ), 	\
		"=m" ( reg ):)

#define interrapt_err_in()	\
	asm("cli");	\
	asm("movl 4(%%ebp), %0":"=r" (INT_ERR));	\
	asm("pushal");	\
	asm("cld");	\
	asm("push %ax");	\
	asm("movw $0x68, %ax; movw %ax, %es; movw %ax, %fs; movw %ax, %gs; movw %ax, %ds");	\
	asm("pop %ax");

/*Макрос для выхода из прерывания*/
#define interrapt_out()	\
	asm("movb $0x20, %al");	\
    asm("outb %al, $0x20");	\
	asm("popal");	\
	asm("leave");	\
	asm("push %ax");	\
	asm("movw $0x78, %ax; movw %ax, %es; movw %ax, %fs; movw %ax, %gs; movw %ax, %ds");	\
	asm("pop %ax");	\
	asm("sti");	\
	asm("iret")

#define int_ack()	\
	asm volatile("movb $0x20, %%al	\n	\
		outb	%%al, $0x20"::)

#define int_err_out()	\
	kprintf("out\n");	\
	asm volatile("movb $0x20, %%al	\n	\
		outb	%%al, $0x20	\n	\
		popl	%%fs	\n	\
		popl	%%es	\n	\
		popl	%%ds	\n	\
		popal	\n	\
		leave	\n	\
		addl	$0x4, %%esp	\n	\
		sti	\n	\
		iret"::)

#define interrapt_err_out()	\
	asm("movb $0x20, %al");	\
    asm("outb %al, $0x20");	\
	asm("popal");	\
	asm("leave");	\
	asm("popl	%eax");	\
	asm("movw $0x78, %ax; movw %ax, %es; movw %ax, %fs; movw %ax, %gs; movw %ax, %ds");	\
	asm("sti");	\
	asm("iret")

#define sys_enter(reg)	\
	asm volatile("cli	\n	\
		pushal	\n	\
		pushl	%%ds	\n	\
		pushl	%%es	\n	\
		pushl	%%fs	\n	\
		movl	%%esp, %0	\n	\
		cld":	\
		"=m"(reg):)

#define sys_out()	\
	asm volatile("popl	%%fs	\n	\
		popl	%%es	\n	\
		popl	%%ds	\n	\
		popal	\n	\
		leave	\n	\
		sti	\n	\
		iret"::)

#define dummyInterrapt(nr)	\
void dummyInterrapt ## nr(){	\
	interrapt_in();	\
	kprintf("dummy%i\n", nr);	\
	for(;;);	\
	interrapt_out();	\
}

/*Шлюз прерывания*/

struct __attribute__((packed)) InterraptGate{
    unsigned	looffset:16;		//Младшие 2 байта смещения
    unsigned	selector:16;		//Селектор сегмента кода
    unsigned	reserved:8;		//Зарезервированы
    unsigned	type:5;		//Тип дескриптора
    unsigned	dpl:2;		//Уровень привелегий
    unsigned	present:1;		//Бит присутствия
    unsigned	hioffset:16;		//Старшие 2 байта смещения
};

struct IDTR_S{
	uint16 p[3];
};

extern struct IDTR_S IDTR, SIDTR;

class Interrapt{
	InterraptGate	IDT[IDT_MAX];
	public:
		Interrapt(){
			asm("cli");
			outb(i8259A_ICW1, i8259A_M0);
			outb(i8259A_ICW2_M, i8259A_M1);
			outb(i8259A_ICW3_M, i8259A_M1);
			outb(i8259A_ICW4_M, i8259A_M1);
			
			outb(i8259A_ICW1, i8259A_S0);
			outb(i8259A_ICW2_S, i8259A_S1);
			outb(i8259A_ICW3_S, i8259A_S1);
			outb(i8259A_ICW4_S, i8259A_S1);
		}
		
		static inline void intOn(){
			//asm("movb $0x20, %al");
    		//asm("outb %al, $0x20");
			asm("sti");
			//asm("sidt SIDTR");
			//kprintf("SIDTR: %X, %X, %X\n",SIDTR.p[0],SIDTR.p[1],SIDTR.p[2]);
		}
		static inline void intOff(){
			asm("cli");
		}
		void setInterrapt(void (*i_addr)(), uint8 i_nr, uint8 i_type);
		void loadIDTR();
		void * operator new(size_t sz, void *addr);
		void setNullInts();
};

typedef uint32 (*int_func_t)(reg_t *);
#define MAX_SYSCALLS	16
class SystemCall{
	int_func_t sys_calls[MAX_SYSCALLS];
	public:
		int regSysCall(uint32 (*func)(reg_t *), uint32 sys_nr){
			sys_calls[sys_nr] = func;
			return 0;
		}
		inline int_func_t getSysCall(uint32 sys_nr){
			return sys_calls[sys_nr];
		}
};

extern Interrapt *im;
extern SystemCall *sc;

extern void gp_interrapt();
extern void np_interrapt();
extern void sys_interrapt();
extern void nop_interrapt();
#endif	/* INTERRAPT_H */

